//------------------------------------------------------------
//  Filename: eth_mac_tx.sv
//   
//  Author  : wlduan@gmail.com
//  Revise  : 2020-11-30 15:00
//  Description: 
//   
//  All Rights Reserved.                                       
//-------------------------------------------------------------
//
`timescale 1ns/1ps
 
module eth_mac_tx ( 
    input  logic       rstn_i,

    input  logic       tx_clk,
    output logic[3:0]  tx_data,
    output logic       tx_data_v,

    input  logic       mac_cfg_fes,  // full duplex only
                                                                     
    input  logic[7:0]  mac_tx_data,
    input  logic       mac_tx_valid, //is Transmit packet  under-run
    input  logic       mac_tx_eof, 
    output logic       mac_tx_ready 
);      
//-------------------------------------------------------------
logic  clk_i; 
//-------------------------------------------------------------
assign clk_i = tx_clk;
//-------------------------------------------------------------
logic[15:0]  tx_nible_cnt;
logic        eof_detected;
logic        fcs_end;
logic        ipg_end;
logic        underflow;
//-------------------------------------------------------------
enum logic[7:0] {IDLE,PREAMB,SFD,DATA0,DATA1,FCS,DONE,TXERR} tx_cs,tx_ns; // 
//--------------------------------------------------------
always_ff @ (posedge clk_i,negedge rstn_i) begin
    if(~rstn_i) begin
        tx_cs <= IDLE;
    end
    else begin
        tx_cs <= tx_ns;
    end
end
//--------------------------------------------------------
always_comb begin
    tx_ns = tx_cs;
    case(tx_cs)
        IDLE: begin
            if(mac_tx_valid) tx_ns = PREAMB;
        end
        PREAMB: begin
            if(tx_nible_cnt == 14) tx_ns = SFD;
        end
        SFD: begin
            tx_ns = DATA0;
        end
        DATA0: begin
            if(underflow) tx_ns = TXERR;
            else tx_ns = DATA1;
        end
        DATA1: begin
            if(eof_detected) tx_ns = FCS;
            else tx_ns = DATA0;
        end
        FCS: begin
            if(fcs_end) tx_ns = DONE;
        end
        DONE: begin
            tx_ns = IDLE;
        end
        TXERR: begin
            tx_ns = IDLE;
        end
    endcase
end
//--------------------------------------------------------
logic       crc_en;
logic[3:0]  data_crc;
logic[3:0]  data_cin;
logic[31:0] crc_next;
logic       crc_init;
logic[31:0] crc;
logic       crc_ok;
logic       crc_en_ff1;
//=======================================================
//====
//=======================================================
//assign crc_en   = (tx_cs == DATA0)||(tx_cs == DATA1);
//assign crc_init = (tx_cs == PREAMB)//(tx_cs == SFD);
always_ff @ (posedge clk_i,negedge rstn_i) begin
    if(~rstn_i) begin
        crc_en   <= 'b0;
        crc_init <= 'b0;
    end
    else begin
        crc_en   <= (tx_cs == DATA0)||(tx_cs == DATA1);
        crc_init <= (tx_cs == PREAMB)||(tx_cs == SFD);
    end
end
assign data_cin = (tx_cs == DATA0) ? mac_tx_data[3:0]:mac_tx_data[7:4];
//--------------------------------------------------------
assign {data_crc[0],data_crc[1],data_crc[2],data_crc[3]} = {data_cin[3],data_cin[2],data_cin[1],data_cin[0]};
//--------------------------------------------------------
assign crc_next[0]  =  crc_en & (data_crc[0] ^ crc[28]); 
assign crc_next[1]  =  crc_en & (data_crc[1] ^ data_crc[0] ^ crc[28] ^ crc[29]); 
assign crc_next[2]  =  crc_en & (data_crc[2] ^ data_crc[1] ^ data_crc[0] ^ crc[28] ^ crc[29] ^ crc[30]); 
assign crc_next[3]  =  crc_en & (data_crc[3] ^ data_crc[2] ^ data_crc[1] ^ crc[29] ^ crc[30] ^ crc[31]); 
assign crc_next[4]  = (crc_en & (data_crc[3] ^ data_crc[2] ^ data_crc[0] ^ crc[28] ^ crc[30] ^ crc[31])) ^ crc[0]; 
assign crc_next[5]  = (crc_en & (data_crc[3] ^ data_crc[1] ^ data_crc[0] ^ crc[28] ^ crc[29] ^ crc[31])) ^ crc[1]; 
assign crc_next[6]  = (crc_en & (data_crc[2] ^ data_crc[1] ^ crc[29] ^ crc[30])) ^ crc[ 2]; 
assign crc_next[7]  = (crc_en & (data_crc[3] ^ data_crc[2] ^ data_crc[0] ^ crc[28] ^ crc[30] ^ crc[31])) ^ crc[3]; 
assign crc_next[8]  = (crc_en & (data_crc[3] ^ data_crc[1] ^ data_crc[0] ^ crc[28] ^ crc[29] ^ crc[31])) ^ crc[4]; 
assign crc_next[9]  = (crc_en & (data_crc[2] ^ data_crc[1] ^ crc[29] ^ crc[30])) ^ crc[5]; 
assign crc_next[10] = (crc_en & (data_crc[3] ^ data_crc[2] ^ data_crc[0] ^ crc[28] ^ crc[30] ^ crc[31])) ^ crc[6]; 
assign crc_next[11] = (crc_en & (data_crc[3] ^ data_crc[1] ^ data_crc[0] ^ crc[28] ^ crc[29] ^ crc[31])) ^ crc[7]; 
assign crc_next[12] = (crc_en & (data_crc[2] ^ data_crc[1] ^ data_crc[0] ^ crc[28] ^ crc[29] ^ crc[30])) ^ crc[8]; 
assign crc_next[13] = (crc_en & (data_crc[3] ^ data_crc[2] ^ data_crc[1] ^ crc[29] ^ crc[30] ^ crc[31])) ^ crc[9]; 
assign crc_next[14] = (crc_en & (data_crc[3] ^ data_crc[2] ^ crc[30] ^ crc[31])) ^ crc[10]; 
assign crc_next[15] = (crc_en & (data_crc[3] ^ crc[31])) ^ crc[11]; 
assign crc_next[16] = (crc_en & (data_crc[0] ^ crc[28])) ^ crc[12]; 
assign crc_next[17] = (crc_en & (data_crc[1] ^ crc[29])) ^ crc[13]; 
assign crc_next[18] = (crc_en & (data_crc[2] ^ crc[30])) ^ crc[14]; 
assign crc_next[19] = (crc_en & (data_crc[3] ^ crc[31])) ^ crc[15]; 
assign crc_next[20] =  crc[16]; 
assign crc_next[21] =  crc[17]; 
assign crc_next[22] = (crc_en & (data_crc[0] ^ crc[28])) ^ crc[18]; 
assign crc_next[23] = (crc_en & (data_crc[1] ^ data_crc[0] ^ crc[29] ^ crc[28])) ^ crc[19]; 
assign crc_next[24] = (crc_en & (data_crc[2] ^ data_crc[1] ^ crc[30] ^ crc[29])) ^ crc[20]; 
assign crc_next[25] = (crc_en & (data_crc[3] ^ data_crc[2] ^ crc[31] ^ crc[30])) ^ crc[21]; 
assign crc_next[26] = (crc_en & (data_crc[3] ^ data_crc[0] ^ crc[31] ^ crc[28])) ^ crc[22]; 
assign crc_next[27] = (crc_en & (data_crc[1] ^ crc[29])) ^ crc[23]; 
assign crc_next[28] = (crc_en & (data_crc[2] ^ crc[30])) ^ crc[24]; 
assign crc_next[29] = (crc_en & (data_crc[3] ^ crc[31])) ^ crc[25]; 
assign crc_next[30] =  crc[26]; 
assign crc_next[31] =  crc[27]; 
//--------------------------------------------------------
always_ff @(posedge clk_i,negedge rstn_i) begin
    if(~rstn_i)begin 
        crc <= 32'hffffffff;
    end
    else if(crc_init) begin
        crc <= 32'hffffffff;
    end
    else begin
        crc <= crc_next;
    end
end
//--------------------------------------------------------
always_ff @(posedge clk_i,negedge rstn_i) begin
    if(~rstn_i)begin 
        crc_ok <= 'b0;
    end
    else begin
        crc_ok <= (crc[31:0] == 32'hc704dd7b);  // crc not equal to magic number
    end
end
//--------------------------------------------------------
always_ff @ (posedge clk_i,negedge rstn_i) begin
    if(~rstn_i) begin
        tx_data   <= 4'b0;
    end
    else if(tx_cs == PREAMB)begin
        tx_data   <= 4'h5;
    end
    else if(tx_cs == SFD)begin 
        tx_data <= 4'hD;
    end
    else if(tx_cs == DATA0) begin
        tx_data <= mac_tx_data[3:0];
    end
    else if(tx_cs == DATA1) begin
        tx_data <= mac_tx_data[7:4];
    end
    else if(tx_cs == FCS) begin
        tx_data <= {~crc_next[28], ~crc_next[29], ~crc_next[30], ~crc_next[31]};
    end
    else begin
        tx_data <= 4'b0;
    end
end
//--------------------------------------------------------
always_ff @ (posedge clk_i,negedge rstn_i) begin
    if(~rstn_i) begin
        tx_data_v <= 1'b0;
    end
    else if ((tx_cs == PREAMB)
           ||(tx_cs == SFD)
           ||(tx_cs == DATA0)
           ||(tx_cs == DATA1)
           ||(tx_cs == FCS)) begin
        tx_data_v <= 1'b1;
    end
    else begin
        tx_data_v <= 1'b0;
    end
end
//--------------------------------------------------------
assign mac_tx_ready = (tx_cs == DATA0);
//--------------------------------------------------------
always_ff @ (posedge clk_i,negedge rstn_i) begin
    if(~rstn_i) begin
        tx_nible_cnt <= 'b0;
    end
    else if((tx_cs == DONE)||(tx_cs == IDLE)||(tx_cs == TXERR)||eof_detected) begin 
        tx_nible_cnt <= 'b0;
    end
    else begin
        tx_nible_cnt <= tx_nible_cnt + 1'b1;
    end
end
//--------------------------------------------------------
always_ff @ (posedge clk_i,negedge rstn_i) begin
    if(~rstn_i) begin
        eof_detected <= 1'b0;
    end
    else if(mac_tx_ready&mac_tx_valid&mac_tx_eof)begin
        eof_detected <= 1'b1;
    end
    else if(eof_detected&(tx_cs == DATA1))begin
        eof_detected <= 1'b0;
    end
end
//--------------------------------------------------------
assign fcs_end = ((tx_cs == FCS)&&(tx_nible_cnt == 7));


endmodule


